package su.myspringapps.onlineschool.repository;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import su.myspringapps.onlineschool.entity.Student;

import javax.persistence.NoResultException;
import javax.persistence.criteria.*;
import java.util.List;

import static org.hibernate.resource.transaction.spi.TransactionStatus.ACTIVE;
import static org.hibernate.resource.transaction.spi.TransactionStatus.MARKED_ROLLBACK;

/**
 * Класс JPACriteriaAPIStudentRepository, расширяет класс {@link StudentRepository}
 * <p/>
 * Выполняет те же функции, что и {@link StudentRepository}, только с помощью <b>JPA Criteria API</b>
 * (нужен для изучения последнего)
 * <p/>
 *
 * @author petr.mikhailin
 * created 02.06.2024 13:53
 */
@Repository
public class JPACriteriaAPIStudentRepository extends StudentRepository {

    @Autowired
    public JPACriteriaAPIStudentRepository(SessionFactory sessionFactory) {
        super(sessionFactory);
    }

    public void updateParameter(Integer id, String paramName, Object paramValue) throws Exception {
        Session session = sessionFactory.openSession();
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaUpdate<Student> criteriaUpdate = criteriaBuilder.createCriteriaUpdate(Student.class);
        Root<Student> root = criteriaUpdate.from(Student.class);
        Transaction transaction = session.beginTransaction();
        try {
            session.createQuery(criteriaUpdate.set(paramName, paramValue).where(criteriaBuilder.equal(
                    root.get("id"), id))).executeUpdate();
        } catch (Exception e) {
            if (transaction.getStatus() == ACTIVE || transaction.getStatus() == MARKED_ROLLBACK) {
                transaction.rollback();
            }
            throw e;
        } finally {
            transaction.commit();
            session.close();
        }
    }


    public void deleteById(Integer id) throws Exception {
        Session session = sessionFactory.openSession();
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaDelete<Student> criteriaDelete = criteriaBuilder.createCriteriaDelete(Student.class);
        Root<Student> root = criteriaDelete.from(Student.class);
        Transaction transaction = session.beginTransaction();
        try {
            session.createQuery(criteriaDelete.where(criteriaBuilder.equal(root.get("id"), id))).executeUpdate();
        } catch (Exception e) {
            if (transaction.getStatus() == ACTIVE || transaction.getStatus() == MARKED_ROLLBACK) {
                transaction.rollback();
            }
            throw e;
        } finally {
            if (transaction.getStatus().equals(TransactionStatus.ACTIVE)) {
                transaction.commit();
            }
            session.close();
        }
    }

    @Override
    public Student findById(Integer id) throws Exception {
        Session session = sessionFactory.openSession();
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Student> criteriaQuery = criteriaBuilder.createQuery(Student.class);
        Root<Student> root = criteriaQuery.from(Student.class);
        try {
            return session.createQuery(criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("id"), id)))
                    .getSingleResult();
        } catch (NoResultException noResultException) {
            return null;
        } finally {
            session.close();
        }
    }

    @Override
    public List<Student> findAll() throws Exception {
        Session session = sessionFactory.openSession();
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Student> criteriaQuery = criteriaBuilder.createQuery(Student.class);
        Transaction transaction = session.beginTransaction();
        try {
            return session.createQuery(criteriaQuery.select(criteriaQuery.from(Student.class))).getResultList();
        } catch (Exception e) {
            if (transaction.getStatus() == ACTIVE || transaction.getStatus() == MARKED_ROLLBACK) {
                transaction.rollback();
            }
            throw e;
        } finally {
            transaction.commit();
            session.close();
        }
    }
}
